<template>
    <popup :keepAlive="false" :maskClose="true" ref="loginPopupRef">
        <div class="loginPopup" v-loading="loading">
            <div class="header">
                <div v-if="loginType === 'email' || loginType === 'phone'" class="back" @click="handleBack">
                    <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-chevron-left" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z" />
                    </svg>
                </div>
                <div class="title">
                    <span v-if="loginType === 'normal'">
                        <span v-if="state == 0 || state == 3">{{ $t("login.title.login") }}</span>
                        <span v-else>{{ $t("login.title.register") }}</span>
                    </span>
                    <span v-else-if="loginType === 'email'">{{ $t("login.title.emailLogin") }}</span>
                    <span v-else>{{ $t("login.title.phoneLogin") }}</span>
                </div>
            </div>

            <!-- 普通登录表单 -->
            <template v-if="loginType === 'normal'">
                <div class="input">
                    <input ref="accountRef" v-if="state == 0" v-model="account" @keydown.enter="handleButton" :maxlength="10" type="text" :placeholder="$t('login.placeholder.account')" autofocus />
                    <input
                        ref="passwordRef"
                        v-else-if="state == 1 || state == 3"
                        v-model="password"
                        @keydown.enter="handleButton"
                        :maxlength="16"
                        type="text"
                        :placeholder="$t('login.placeholder.password')"
                    />
                    <input
                        ref="rePasswordRef"
                        v-else-if="state == 2"
                        v-model="rePassword"
                        @keydown.enter="handleButton"
                        :maxlength="16"
                        type="text"
                        :placeholder="$t('login.placeholder.rePassword')"
                    />
                </div>
                <div class="button flex items-center justify-center" @click="handleButton">
                    <span v-if="state == 0 || state == 1">{{ $t("login.button.next") }}</span>
                    <span v-if="state == 2">{{ $t("login.button.login") }}</span>
                    <span v-if="state == 3">{{ $t("login.button.signIn") }}</span>
                </div>
                <div class="divider flex items-center justify-center">
                    {{ $t("login.divider") }}
                </div>
                <!-- 第三方登录 -->
                <div class="thirdParty">
                    <!-- qq登录 -->
                    <el-tooltip :content="$t('login.tooltip.qq')">
                        <div class="item" @click="handleThirdParty('qq')">
                            <div class="icon">
                                <svg t="1741181266315" class="qq" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5251" xmlns:xlink="http://www.w3.org/1999/xlink">
                                    <path
                                        d="M824.8 613.2c-16-51.4-34.4-94.6-62.7-165.3C766.5 262.2 689.3 112 511.5 112 331.7 112 256.2 265.2 261 447.9c-28.4 70.8-46.7 113.7-62.7 165.3-34 109.5-23 154.8-14.6 155.8 18 2.2 70.1-82.4 70.1-82.4 0 49 25.2 112.9 79.8 159-26.4 8.1-85.7 29.9-71.6 53.8 11.4 19.3 196.2 12.3 249.5 6.3 53.3 6 238.1 13 249.5-6.3 14.1-23.8-45.3-45.7-71.6-53.8 54.6-46.2 79.8-110.1 79.8-159 0 0 52.1 84.6 70.1 82.4 8.5-1.1 19.5-46.4-14.5-155.8z"
                                        p-id="5252"
                                        fill="currentColor"
                                    ></path>
                                </svg>
                            </div>
                        </div>
                    </el-tooltip>
                    <!-- 手机验证码登录 暂时隐藏 阿里云短信已不可用 -->
                    <!-- <el-tooltip :content="$t('login.tooltip.phone')">
                        <div class="item" @click="handleThirdParty('phone')">
                            <div class="icon">
                                <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
                                    <path d="M11 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6zM5 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H5z" />
                                    <path d="M8 14a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" />
                                </svg>
                            </div>
                        </div>
                    </el-tooltip> -->
                    <!-- 邮箱验证码登录 -->
                    <el-tooltip :content="$t('login.tooltip.email')">
                        <div class="item" @click="handleThirdParty('email')">
                            <div class="icon">
                                <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-envelope" viewBox="0 0 16 16">
                                    <path
                                        d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2Zm13 2.383-4.708 2.825L15 11.105V5.383Zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741ZM1 11.105l4.708-2.897L1 5.383v5.722Z"
                                    />
                                </svg>
                            </div>
                        </div>
                    </el-tooltip>
                </div>
            </template>

            <!-- 邮箱登录表单 -->
            <template v-else-if="loginType === 'email'">
                <div class="input">
                    <div class="email-input">
                        <input v-model="email" type="email" :placeholder="$t('login.placeholder.email')" :disabled="cooldown.email > 0" />
                        <div class="send-code" @click="handleSendCode" :class="{ disabled: cooldown.email > 0 }">
                            {{ cooldown.email > 0 ? cooldown.email + "s" : hasSentCode.email ? $t("login.button.resendCode") : $t("login.button.sendCode") }}
                        </div>
                    </div>
                </div>
                <div class="input">
                    <input @keydown.enter="handleEmailLogin" v-model="verifyCode.email" type="text" :placeholder="$t('login.placeholder.verifyCode')" />
                </div>
                <div class="button flex items-center justify-center" @click="handleEmailLogin">
                    {{ $t("login.button.emailLogin") }}
                </div>
            </template>

            <!-- 手机登录表单 -->
            <template v-else-if="loginType === 'phone'">
                <div class="input">
                    <div class="email-input">
                        <input v-model="phone" type="tel" :placeholder="$t('login.placeholder.phone')" :disabled="cooldown.phone > 0" />
                        <div class="send-code" @click="handleSendPhoneCode" :class="{ disabled: cooldown.phone > 0 }">
                            {{ cooldown.phone > 0 ? cooldown.phone + "s" : hasSentCode.phone ? $t("login.button.resendCode") : $t("login.button.sendCode") }}
                        </div>
                    </div>
                </div>
                <div class="input">
                    <input @keydown.enter="handlePhoneLogin" v-model="verifyCode.phone" type="text" :placeholder="$t('login.placeholder.verifyCode')" />
                </div>
                <div class="button flex items-center justify-center" @click="handlePhoneLogin">
                    {{ $t("login.button.phoneLogin") }}
                </div>
            </template>
        </div>
    </popup>
</template>

<script lang="ts" setup>
import { ref, defineExpose, nextTick, defineEmits, onUnmounted } from "vue"
import fetch from "@/api/fetch.js"
import useAppStore from "@/pinia"
import { useI18n } from "vue-i18n"
import { isDev } from "@/utils/env"

const app = useAppStore()
const { t } = useI18n()
const loginPopupRef = ref()
const account = ref("")
const password = ref("")
const rePassword = ref("")
const state = ref(0) // 显示状态 0: 输入账号 1: 注册 2:再次输入密码 3: 登录
const accountRef = ref()
const passwordRef = ref()
const rePasswordRef = ref()
const loading = ref(false)
const loginType = ref("normal") // 'normal' | 'email' | 'phone'
const email = ref("")
const phone = ref("")
const verifyCode = ref({
    email: "",
    phone: "",
})
const cooldown = ref({
    email: 0,
    phone: 0,
})
const hasSentCode = ref({
    email: false,
    phone: false,
})
const phoneRegex = /^1[3-9]\d{9}$/ // 中国大陆手机号格式
const hasCaptcha = ref(false)
const captchaBase = ref()
const ws = ref<WebSocket | null>(null)

const emit = defineEmits(["loginEnd"])

// 邮箱格式验证正则
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

const show = () => {
    loginPopupRef.value.show()
    nextTick(() => {
        // 自动聚焦
        accountRef.value.focus()
    })
}

const hide = () => {
    closePopup()
    loginPopupRef.value.closePopup()
}

const clearAllStates = () => {
    // 清除普通登录状态
    account.value = ""
    password.value = ""
    rePassword.value = ""
    state.value = 0
    loginType.value = "normal"

    // 处理邮箱登录状态
    if (cooldown.value.email === 0) {
        email.value = ""
        verifyCode.value.email = ""
        hasSentCode.value.email = false
    }

    // 处理手机登录状态
    if (cooldown.value.phone === 0) {
        phone.value = ""
        verifyCode.value.phone = ""
        hasSentCode.value.phone = false
    }

    hasCaptcha.value = false
}

const closePopup = () => {
    clearAllStates()
    loginPopupRef.value.closePopup()
}

// qq登录回调
const watchQQLoginSuccess = (event: MessageEvent) => {
    // 安全验证：来源必须匹配
    if (event.origin !== window.location.origin) return console.log("非法来源", event.origin)
    if (event.data.type === "qqLoginSuccess") {
        console.log("QQ登录成功", event.data)
        const { token } = event.data
        loading.value = true
        fetch("qqLogin", { token })
            .then((res: any) => {
                console.log("qqLogin", res)
                let { user, qlKey } = res
                app.isLogin = true
                app.updateUserInfo(user)
                app.showTopMessage(t("login.success.login"), "success")
                hide()
                emit("loginEnd")
                localStorage.setItem("qlKey", qlKey) // 更新 qlKey
            })
            .catch((err: any) => {
                console.log("err", err)
                app.showMessage(err.msg)
            })
            .finally(() => (loading.value = false))
    }
}

const qqLoginTest = () => {
    const token = "83F32BDA2C649F3B1D71865643648C61"
    loading.value = true
    fetch("qqLogin", { token })
        .then((res: any) => {
            console.log("qqLogin", res)
            let { user, qlKey } = res
            app.isLogin = true
            app.updateUserInfo(user)
            app.showTopMessage(t("login.success.login"), "success")
            hide()
            emit("loginEnd")
            localStorage.setItem("qlKey", qlKey) // 更新 qlKey
        })
        .catch((err: any) => {
            console.log("qqLogin err", err)
            app.showMessage(err.msg)
        })
        .finally(() => (loading.value = false))
}

// 开始qq登录
const startQQLogin = () => {
    if (isDev) qqLoginTest()
    else {
        const appId = "102786285"
        let url = window.location.origin + "/qqCallBack.html"
        let redirectUri = encodeURIComponent(url)
        const state = Math.random().toString(36).substring(2) // 防CSRF
        // 弹出QQ登录窗口
        window.open(
            `https://graph.qq.com/oauth2.0/authorize?response_type=token&client_id=${appId}&redirect_uri=${redirectUri}&state=${state}&display=popup`,
            "QQLogin",
            "width=600,height=600"
        ) as Window
        // 监听回调页发来的消息
        window.addEventListener("message", watchQQLoginSuccess)
    }
}

// 点击登录按钮
const handleButton = () => {
    if (state.value == 0) checkAccount()
    else if (state.value == 1) {
        if (password.value == "") return app.showMessage(t("login.error.emptyPassword"))
        state.value = 2
        nextTick(() => {
            rePasswordRef.value.focus()
        })
    } else if (state.value == 2) register()
    else if (state.value == 3) login()
}

const checkAccount = () => {
    if (account.value == "") return app.showMessage(t("login.error.emptyAccount"))
    loading.value = true
    fetch("checkAccount", { account: account.value })
        .then((res: any) => {
            let { has = 1, ok = 1 } = res
            if (ok) {
                if (has) state.value = 3
                else state.value = 1
                nextTick(() => passwordRef.value.focus())
            } else app.showMessage(t("login.error.invalidAccount"))
        })
        .finally(() => (loading.value = false))
}

const register = () => {
    if (password.value != rePassword.value) return app.showMessage(t("login.error.passwordMismatch"))
    loading.value = true
    fetch("register", { account: account.value, password: password.value })
        .then((res: any) => {
            if (res.hasAccount) app.showMessage(t("login.error.accountExists"))
            else login()
        })
        .catch((err: any) => {
            console.log("err", err)
            app.showMessage(err.msg)
        })
        .finally(() => (loading.value = false))
}

const login = () => {
    loading.value = true
    fetch("login", { account: account.value, password: password.value })
        .then((res: any) => {
            let { user, passwordOk, qlKey } = res
            if (passwordOk) {
                app.isLogin = true
                app.updateUserInfo(user)
                app.showTopMessage(t("login.success.login"), "success")
                hide()
                emit("loginEnd")
                localStorage.setItem("qlKey", qlKey) // 更新 qlKey
            } else app.showMessage(t("login.error.wrongPassword"))
        })
        .catch((err: any) => {
            console.log("err", err)
            app.showMessage(err.msg)
        })
        .finally(() => (loading.value = false))
}

const handleThirdParty = (type: string) => {
    if (type === "email") {
        loginType.value = "email"
    } else if (type === "phone") {
        loginType.value = "phone"
        nextTick(() => {
            try {
                initGeetest()
            } catch (error) {
                console.error(error)
            }
        })
    } else if (type === "qq") {
        startQQLogin()
    }
}

const handleBack = () => {
    // 只切换登录类型，保留邮箱表单状态
    loginType.value = "normal"
}

const handleSendCode = () => {
    if (cooldown.value.email > 0) return
    if (!email.value) return app.showMessage(t("login.error.emptyEmail"))
    if (!emailRegex.test(email.value)) return app.showMessage(t("login.error.invalidEmail"))

    loading.value = true
    fetch("sendVerificationCode", { email: email.value, action: "emailLogin" })
        .then(() => {
            app.showMessage(t("login.message.codeSent"), 2, "success")
            hasSentCode.value.email = true
            cooldown.value.email = 60
            const timer = setInterval(() => {
                cooldown.value.email--
                if (cooldown.value.email <= 0) clearInterval(timer)
            }, 1000)
        })
        .catch((err: any) => app.showMessage(err.msg || t("login.error.sendCodeFailed")))
        .finally(() => (loading.value = false))
}

const handleEmailLogin = () => {
    if (!email.value) return app.showMessage(t("login.error.emptyEmail"))
    if (!emailRegex.test(email.value)) return app.showMessage(t("login.error.invalidEmail"))
    if (!verifyCode.value.email) return app.showMessage(t("login.error.emptyCode"))
    loading.value = true
    fetch("verifyVerificationCode", { email: email.value, verificationCode: verifyCode.value.email })
        .then((res: any) => {
            let { user, qlKey, msg = "ok" } = res
            if (msg == "ok") {
                app.isLogin = true
                app.updateUserInfo(user)
                app.showTopMessage(t("login.success.login"), "success")
                hide()
                emit("loginEnd")
                localStorage.setItem("qlKey", qlKey)
            } else if (msg == "invalid_code") {
                app.showMessage(t("login.error.invalidCode"))
            } else {
                app.showMessage(msg || t("login.error.verifyCodeFailed"))
            }
        })
        .catch((err: any) => {
            app.showMessage(err.msg || t("login.error.verifyCodeFailed"))
        })
        .finally(() => (loading.value = false))
}

const handleSendPhoneCode = () => {
    if (cooldown.value.phone > 0) return
    if (!phone.value) return app.showMessage(t("login.error.emptyPhone"))
    if (!phoneRegex.test(phone.value)) return app.showMessage(t("login.error.invalidPhone"))
    if (hasCaptcha.value) {
        captchaBase.value.onSuccess(() => {
            // 本地校验成功
            const captchaResult = captchaBase.value.getValidate()
            fetch("geetestValidate", { ...captchaResult })
                .then((res: any) => {
                    if (res.result == "success") success() // 二次校验成功
                    else {
                        app.showMessage(t("login.error.pleaseTryAgain"))
                        captchaBase.value.reset()
                    }
                })
                .catch(() => {
                    app.showMessage(t("login.error.pleaseTryAgain"))
                    captchaBase.value.reset()
                })
        })
        captchaBase.value.showCaptcha()
    } else return app.showMessage("please wait for captcha")
    const success = () => {
        loading.value = true
        fetch("sms", { phoneNumbers: phone.value, action: "smsLogin" })
            .then(() => {
                app.showMessage(t("login.message.codeSent"), 2, "success")
                hasSentCode.value.phone = true
                cooldown.value.phone = 60
                const timer = setInterval(() => {
                    cooldown.value.phone--
                    if (cooldown.value.phone <= 0) clearInterval(timer)
                }, 1000)
                loading.value = false
            })
            .catch((err: any) => {
                app.showMessage(err.msg || t("login.error.sendCodeFailed"))
            })
            .finally(() => (loading.value = false))
    }
}
const handlePhoneLogin = () => {
    if (!phone.value) return app.showMessage(t("login.error.emptyPhone"))
    if (!phoneRegex.test(phone.value)) return app.showMessage(t("login.error.invalidPhone"))
    if (!verifyCode.value.phone) return app.showMessage(t("login.error.emptyCode"))

    loading.value = true
    // 模拟验证码验证请求
    setTimeout(() => {
        const mockUser = {
            id: "123",
            name: "Phone User",
            avatar: "",
        }
        const mockQlKey = "mock_ql_key"

        app.isLogin = true
        app.updateUserInfo(mockUser)
        app.showTopMessage(t("login.success.login"), "success")
        hide()
        emit("loginEnd")
        localStorage.setItem("qlKey", mockQlKey)
        loading.value = false
    }, 1000)
}

defineExpose({
    show,
    hide,
})

// 声明全局变量 避免ts报错
declare global {
    interface Window {
        initGeetest4: Function
    }
}

// 初始化极验
const initGeetest = () => {
    const captchaId = "f31a088f46551a1f8d55c2e28ef8fff3"
    if (window.initGeetest4) {
        window.initGeetest4(
            {
                captchaId,
                product: "bind", // 可以根据需求修改产品类型
            },
            (captcha: any) => {
                captchaBase.value = captcha
                    .onReady(function () {
                        hasCaptcha.value = true
                    })
                    .onError(function () {
                        console.log("Geetest initialization failed.")
                    })
            }
        )
    } else console.log("Geetest initialization failed.")
}

// 在组件卸载时关闭 WebSocket 连接
onUnmounted(() => {
    if (ws.value) {
        ws.value.close()
    }
    // 取消message事件
    window.removeEventListener("message", watchQQLoginSuccess)
})
</script>

<style lang="scss">
.loginPopup {
    width: 50vw;
    border-radius: 1vmin;
    background-image: linear-gradient(70deg, rgb(9, 32, 63, 0.95) 0%, rgba(28, 50, 83, 0.95) 100%);
    padding: 5vmin 8vmin;
    box-sizing: border-box;
    position: relative;

    .header {
        margin-bottom: 3vmin;

        .back {
            position: absolute;
            left: 2vmin;
            top: 2vmin;
            color: #aaa;
            cursor: pointer;
            width: 6vmin;
            height: 6vmin;
            display: flex;
            align-items: center;
            justify-content: center;

            svg {
                width: 5vmin;
                height: 5vmin;
            }

            &:hover {
                color: #fff;
            }
        }
    }

    .title {
        text-align: center;
        color: #ddd;
        font-size: 8vmin;
        font-weight: 600;
        letter-spacing: 1vmin;
        font-family: Floane;
    }

    .label {
        color: #bbb;
        font-size: 4vmin;
        letter-spacing: 1vmin;
        margin-top: 3vmin;
    }

    .input {
        width: 100%;
        height: 14vmin;
        box-shadow: inset 0 0 1vmin 1vmin rgba(0, 0, 0, 0.05);
        background-color: rgba(255, 255, 255, 0.01);
        margin-top: 3vmin;
        border-radius: 1vmin;
        border-bottom: 1px solid rgba(255, 255, 255, 0.1);
        padding: 2vmin;
        box-sizing: border-box;

        &:not(:first-child) {
            margin-top: 2vmin;
        }

        input {
            width: 100%;
            height: 100%;
            padding: 0 2vmin;
            color: #aaa;
            font-size: 5vmin;
            letter-spacing: 0.1vmin;
            box-sizing: border-box;

            &::placeholder {
                color: rgb(170, 170, 170, 0.5);
            }

            &:disabled {
                opacity: 0.5;
                cursor: not-allowed;
            }
        }

        .email-input {
            display: flex;
            gap: 2vmin;
            height: 100%;
            box-sizing: border-box;

            input {
                flex: 1;
            }

            .send-code {
                width: 25%;
                height: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
                background: rgba(88, 101, 242, 0.3);
                border-radius: 1vmin;
                color: #ddd;
                cursor: pointer;
                font-size: 2.5vmin;
                transition: all 0.3s;

                &:hover:not(.disabled) {
                    background: rgba(76, 90, 232, 0.5);
                }

                &.disabled {
                    cursor: not-allowed;
                    opacity: 0.5;
                }
            }
        }
    }

    .button {
        width: 100%;
        height: 12vmin;
        border-radius: 1vmin;
        color: #ddd;
        font-size: 4vmin;
        letter-spacing: 1vmin;
        background: rgb(88, 101, 242, 0.3);
        margin-top: 4vmin;
        cursor: pointer;
        transition: all 0.4s ease-in-out;

        &:hover {
            background: rgb(76, 90, 232, 5);
        }
    }

    .divider {
        margin: 6vmin 0 4vmin;
        color: rgba(255, 255, 255, 0.3);
        font-size: 3vmin;
        gap: 2vmin;

        &::before,
        &::after {
            content: "";
            height: 1px;
            flex: 1;
            background: linear-gradient(to var(--direction, right), transparent, rgba(255, 255, 255, 0.1) 20%, rgba(255, 255, 255, 0.1) 80%, transparent);
        }

        &::before {
            --direction: right;
        }

        &::after {
            --direction: left;
        }
    }

    .thirdParty {
        display: flex;
        justify-content: center;
        gap: 4vmin;

        .item {
            width: 10vmin;
            height: 10vmin;
            border-radius: 50%;
            background: rgba(255, 255, 255, 0.05);
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: all 0.3s;
            filter: drop-shadow(0 0 1vmin rgba(255, 255, 255, 0.5));
            color: #bbb;

            .icon {
                font-size: 5vmin;
                font-weight: bold;

                svg {
                    width: 6vmin;
                    height: 6vmin;
                    transition: all 0.3s;
                }
            }

            &:hover {
                background: rgba(255, 255, 255, 0.1);
                transform: translateY(-0.5vmin);
                filter: drop-shadow(0 0 1vmin rgba(0, 255, 255, 0.5));

                .icon {
                    .qq {
                        color: rgba(0, 255, 255, 0.5);
                    }
                }
            }
        }
    }
}
</style>
